//******************************************************************
//******************************************************************
//**********          ANts Peer To Peer Sources        *************
//
// ANts P2P realizes a third generation P2P net. It protects your
// privacy while you are connected and makes you not trackable, hiding
// your identity (ip) and crypting everything you are sending/receiving
// from others.

// Copyright (C) 2004  Roberto Rossi

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

package ants.p2p.http;

import java.beans.*;
import java.net.*;
import java.io.*;
import java.util.*;

import ants.p2p.*;
import ants.p2p.messages.*;
import ants.p2p.filesharing.*;

import org.apache.log4j.*;

public class HttpRequestHandler
    extends Thread implements PropertyChangeListener{
  HttpRequestMessage requestMessage;
  WarriorAnt wa;
  int currentId = 0;
  boolean terminate = false;

  static Logger _logger = Logger.getLogger(HttpRequestHandler.class.getName());

  public HttpRequestHandler(HttpRequestMessage requestMessage, WarriorAnt wa) {
    this.wa = wa;
    this.wa.propertyChangeSupport.addPropertyChangeListener(this);
    this.requestMessage = requestMessage;
    this.start();
  }

  public void propertyChange(PropertyChangeEvent e){
    if (e.getPropertyName().equals("httpInterruptTransfer") &&
        e.getNewValue().equals(this.requestMessage)) {
      this.terminate();
    }
  }

  public WarriorAnt getWarriorAnt() {
    return this.wa;
  }

  public void run() {
    this.handleRequest();
  }

  public boolean equals(Object o) {
    if (o instanceof HttpRequestMessage)
      return this.requestMessage.equals(o);
    else
      return this == o;
  }

  public void terminate(){
    this.terminate = true;
  }

  void handleRequest() {
    Socket s = null;
    OutputStream os = null;
    InputStream is = null;
    try {
      s = new Socket("localhost", WarriorAnt.getHttpLocalServerPort());
      s.setSoTimeout(5000);
      os = s.getOutputStream();
      is = s.getInputStream();
      os.write(requestMessage.getBody());
      s.shutdownOutput();
      byte[] response = new byte[WarriorAnt.blockSizeInDownload];

      _logger.debug("Show Browser Request:");
      String browserRequest = "";
      for (int x = 0; x < requestMessage.getBody().length; x++) {
        browserRequest += ( (char) requestMessage.getBody()[x]);
      }
      _logger.debug(browserRequest);

      _logger.debug("Waiting for Response...");
      int n;
      ArrayList sentMessages = new ArrayList();
      while (!terminate && (n = is.read(response)) != -1) {
        if (n < response.length) {
          byte[] responseShorted = new byte[n];
          for (int x = 0; x < n; x++) {
            responseShorted[x] = response[x];
          }

          ants.p2p.security.EndpointSecurityManager esm = this.getWarriorAnt().
              getInputSecureConnectionManager(this.requestMessage.getSource());
          if (esm == null) {
            SecureConnectionErrorControlMessage fterrcm = new
                SecureConnectionErrorControlMessage(new Integer(0), null,
                "No secure connection");
            MessageWrapper wm = wa.sendMessage(fterrcm, this.requestMessage.getSource(), false, false);
            _logger.debug(wa.getShortId() + ": No secure connection with " +
                          this.requestMessage.getSource());
            while (wa.myMessages.contains(wm) && !this.terminate) {
              Thread.sleep(1000);
            }
            if (wa.failedMessages.contains(wm)) {
              wa.failedMessages.remove(wm);
              _logger.debug(wa.getShortId() +
                            ": Error in sending ControlMessage " +
                            fterrcm.getMessage());
            }
            throw new Exception("No secure connection avaiable with endpoint " +
                                this.requestMessage.getSource());
          }

          HttpResponsePartMessage httpResponsePartMessage = new
              HttpResponsePartMessage(responseShorted, this.requestMessage,
                                      new Integer(currentId++));

          httpResponsePartMessage.encrypt(esm.getCipherEnc());
          MessageWrapper wm = this.getWarriorAnt().sendMessage(
              httpResponsePartMessage, this.requestMessage.getSource(), false, false);
          sentMessages.add(wm);
          while (sentMessages.size() >= 2 &&
                 !this.getWarriorAnt().areInFailedMessages(sentMessages)){
            this.wa.removeDeliveredMessagesFromList(sentMessages);
            Thread.sleep(1000);
          }
          if (this.getWarriorAnt().areInFailedMessages(sentMessages)) {
            this.getWarriorAnt().failedMessages.removeAll(sentMessages);

            _logger.debug(this.getWarriorAnt().getShortId() +
                          ": Error in sending HttpResponsePartMessage" +
                          " - Cannot send to id " +
                          httpResponsePartMessage.getDest());

            this.getWarriorAnt().propertyChangeSupport.firePropertyChange(
                "httpResponsePartError", null, this.requestMessage);
            this.wa.propertyChangeSupport.removePropertyChangeListener(this);
            return;
          }
        }
        else {
          ants.p2p.security.EndpointSecurityManager esm = this.getWarriorAnt().
              getInputSecureConnectionManager(this.requestMessage.getSource());
          if (esm == null) {
            SecureConnectionErrorControlMessage fterrcm = new
                SecureConnectionErrorControlMessage(new Integer(0), null,
                "No secure connection");
            MessageWrapper wm = wa.sendMessage(fterrcm, this.requestMessage.getSource(), false, false);
            _logger.debug(wa.getShortId() + ": No secure connection with " +
                          this.requestMessage.getSource());
            while (wa.myMessages.contains(wm) && !this.terminate) {
              Thread.sleep(1000);
            }
            if (wa.failedMessages.contains(wm)) {
              wa.failedMessages.remove(wm);
              _logger.debug(wa.getShortId() +
                            ": Error in sending ControlMessage " +
                            fterrcm.getMessage());
            }
            throw new Exception("No secure connection avaiable with endpoint " +
                                this.requestMessage.getSource());
          }

          HttpResponsePartMessage httpResponsePartMessage = new
              HttpResponsePartMessage(response, this.requestMessage,
                                      new Integer(currentId++));

          httpResponsePartMessage.encrypt(esm.getCipherEnc());
          MessageWrapper wm = this.getWarriorAnt().sendMessage(
              httpResponsePartMessage, this.requestMessage.getSource(), false, false);
          while (this.getWarriorAnt().myMessages.contains(wm)) {
            Thread.sleep(1000);
          }
          if (this.getWarriorAnt().failedMessages.contains(wm)) {
            httpResponsePartMessage.decrypt(esm.getCipherDec());
            this.getWarriorAnt().failedMessages.remove(wm);

            _logger.debug(this.getWarriorAnt().getShortId() +
                          ": Error in sending HttpResponsePartMessage" +
                          " - Cannot send to id " +
                          httpResponsePartMessage.getDest());

            this.getWarriorAnt().propertyChangeSupport.firePropertyChange(
                "httpResponsePartError", null, this.requestMessage);
            this.wa.propertyChangeSupport.removePropertyChangeListener(this);
            return;
          }
        }
      }
      while (!terminate && sentMessages.size() > 0 && !this.getWarriorAnt().areInFailedMessages(sentMessages)) {
        this.wa.removeDeliveredMessagesFromList(sentMessages);
        Thread.sleep(1000);
      }
      if (this.getWarriorAnt().areInFailedMessages(sentMessages)) {
        this.getWarriorAnt().failedMessages.removeAll(sentMessages);

        _logger.debug(this.getWarriorAnt().getShortId() +
                      ": Error in sending HttpResponsePartMessage" +
                      " - Cannot send to id " +
                      this.requestMessage.getSource());

        this.getWarriorAnt().propertyChangeSupport.firePropertyChange(
            "httpResponsePartError", null, this.requestMessage);
        this.wa.propertyChangeSupport.removePropertyChangeListener(this);
        return;
      }
    }
    catch (Exception e) {
      _logger.error("Error in serving request", e);
    }

    if(terminate){
      try{
        s.shutdownInput();
        s.close();
      }catch(Exception ex){}
      this.wa.propertyChangeSupport.removePropertyChangeListener(this);
      return;
    }

    try {
      ants.p2p.security.EndpointSecurityManager esm = this.getWarriorAnt().
          getInputSecureConnectionManager(this.requestMessage.getSource());
      if (esm == null) {
        SecureConnectionErrorControlMessage fterrcm = new
            SecureConnectionErrorControlMessage(new Integer(0), null,
            "No secure connection");
        MessageWrapper wm = wa.sendMessage(fterrcm, this.requestMessage.getSource(), false, false);
        _logger.debug(wa.getShortId() + ": No secure connection with " +
                      this.requestMessage.getSource());
        while (wa.myMessages.contains(wm) && !this.terminate) {
          Thread.sleep(1000);
        }
        if (wa.failedMessages.contains(wm)) {
          wa.failedMessages.remove(wm);
          _logger.debug(wa.getShortId() +
                        ": Error in sending ControlMessage " +
                        fterrcm.getMessage());
        }
        throw new Exception("No secure connection avaiable with endpoint " +
                            this.requestMessage.getSource());
      }

      HttpTransferEndControlMessage httpTransferEndControlMessage = new
          HttpTransferEndControlMessage(new Integer(0), null,
                                        "HttpTransfer ended",
                                        this.requestMessage);

      httpTransferEndControlMessage.encrypt(esm.getCipherEnc());
      MessageWrapper wm = this.getWarriorAnt().sendMessage(
          httpTransferEndControlMessage, this.requestMessage.getSource(), false, false);
      while (this.getWarriorAnt().myMessages.contains(wm)) {
        Thread.sleep(1000);
      }
      if (this.getWarriorAnt().failedMessages.contains(wm)) {
        httpTransferEndControlMessage.decrypt(esm.getCipherDec());
        this.getWarriorAnt().failedMessages.remove(wm);

        _logger.debug(this.getWarriorAnt().getShortId() +
                      ": Error in sending HttpResponsePartMessage" +
                      " - Cannot send to id " +
                      httpTransferEndControlMessage.getDest());

        this.getWarriorAnt().propertyChangeSupport.firePropertyChange(
            "httpResponsePartError", null, this.requestMessage);
        this.wa.propertyChangeSupport.removePropertyChangeListener(this);
        return;
      }
      _logger.debug("End of Response");
      s.shutdownInput();
      s.close();
    }
    catch (Exception e) {
      _logger.error("Error in sending end of response", e);
    }
    this.wa.propertyChangeSupport.removePropertyChangeListener(this);
  }
}
